Seattle Pets top of page

Author: Jessica Marx

Date: 01 August 2019

# load packages, install if needed
packages = c(
      "dplyr"
    , "ggplot2"
    , "formattable"
    , "plotly"
    , "RColorBrewer"
    , "scales"
    , "stringr"
    , "tidyr"
    , "ElmeR"
    , "RJDBC"
    , "kableExtra"
    , "wesanderson"
    , "reshape2"
    , "rtweet"
    , "tidytext"
    , "lubridate"
    , "wordcloud"
    , "ggpubr"
    , "ggthemes"
    , "knitrBootstrap"
    , "DT"
    , "MatchIt"
    , "beyonce"
    , "UpSetR"
    , "gganimate"
    , "wordcloud2"
    , "widyr"
    , "ggraph"
    , "igraph"
    , "aod"
    , "corrplot"
    , "ROCR"
    , "InformationValue"
    , "car"
    , "glmnet"
    , "caret"
    , "kernlab"
    , "pdp"
    , "rpart.plot"
    , "rpart"
    , "e1071"
    )
package.check <- lapply(packages, FUN = function(x) {
  if (!require(x, character.only = TRUE)) {
    install.packages(x, dependencies = TRUE)
    library(x, character.only = TRUE)
  }
})
options(scipen= 999)
theme_set(theme_minimal(base_size = 9, base_family = "Roboto"))
#functions!
#round 
round.to <- function(x, b) {
  round(x/b)*b
}
#odds to probability
odds.to.prob <- function(odds) {
  odds/(1 + odds) 
}
#log odds to probability 
logit2prob <- function(logit){
  odds <- exp(logit)
  prob <- odds / (1 + odds)
  return(prob)
}
#convert to a range
range01 <- function(x){
  (x-min(x))/(max(x)-min(x))
}
#function to get vector of color values from RColorBrewer
get_hex_values <- function(pal) {
    brewer.pal(brewer.pal.info[pal, "maxcolors"], pal)
}
paired_cols <- get_hex_values(pal = "Paired")

Top Dog Breeds

a = seattle_pet_licenses %>% 
  filter(species == "Dog") %>% 
  select(primary_breed, license_issue_date) %>% 
  mutate(license_issue_date = as.Date(license_issue_date),
         year = lubridate::year(license_issue_date)) %>% 
  group_by(primary_breed, year) %>% 
  count() %>% 
  arrange(year, desc(n)) %>% 
  ungroup() %>% 
  group_by(year) %>% 
  mutate(rank = row_number()) %>% 
  filter(rank <= 5, year >= 2010) %>%
  mutate(rev_rank = rank(-rank)) %>% 
  ungroup() 
colourCount = length(unique(a$primary_breed))
getPalette = colorRampPalette(brewer.pal(12, "Paired"))
ggplot(a, aes(x = year, y = reorder(rev_rank, rev_rank), fill = primary_breed)) +
  geom_bar(stat = "identity", position = "stack", color = "white") + 
  geom_text(stat = "identity", position = "stack", aes(label = rank), color = "black", size = 3, vjust = 1, hjust = 3) + 
  theme(axis.text.x=element_blank(),
        axis.ticks.x=element_blank()) +
  scale_x_continuous(breaks = seq(2010, 2016, 1)) + 
  coord_flip() +
  ylab("rank") + 
  scale_fill_manual(values = getPalette(colourCount), name = "Breed") +  
  ggtitle(label = "Top Five Seattle Dog Breeds by Year")

Breed Acceleration

a = seattle_pet_licenses %>% 
  filter(species == "Dog") %>% 
  select(primary_breed, license_issue_date) %>% 
  mutate(license_issue_date = as.Date(license_issue_date),
         year = lubridate::year(license_issue_date)) %>% 
  filter(year >= 2015) %>% 
  group_by(primary_breed, year) %>% 
  count() %>%
  spread(year, n) %>% 
  drop_na() %>% 
  mutate(pct_increase = `2016`/`2015`-1) %>% 
  arrange(desc(pct_increase)) %>% 
  head(20) %>% 
  ungroup()
b = seattle_pet_licenses %>% 
  filter(species == "Dog") %>% 
  select(secondary_breed, license_issue_date) %>% 
  mutate(license_issue_date = as.Date(license_issue_date),
         year = lubridate::year(license_issue_date)) %>% 
  filter(year >= 2015) %>% 
  group_by(secondary_breed, year) %>% 
  count() %>%
  spread(year, n) %>% 
  drop_na() %>% 
  mutate(pct_increase = `2016`/`2015`-1) %>% 
  arrange(desc(pct_increase)) %>% 
  head(20) %>% 
  ungroup()
breeds = a %>% 
  select(primary_breed, pct_increase) %>% 
  rename("breed" = primary_breed) %>% 
  mutate(breed_level = "Primary Breed") %>% 
  rbind(
    b %>% 
  select(secondary_breed, pct_increase) %>% 
  rename("breed" = secondary_breed) %>% 
  mutate(breed_level = "Secondary Breed")
  ) %>% 
  group_by(breed_level) %>% 
  arrange(breed_level, desc(pct_increase)) %>% 
  mutate(rank = row_number()) %>% 
  ungroup()
  
colourCount = length(unique(breeds$breed))
getPalette = colorRampPalette(brewer.pal(12, "Paired"))
breeds %>% 
  ggplot(aes(x = reorder(breed, -rank), y = pct_increase, fill = breed)) +
  geom_bar(stat = "identity") + 
  scale_fill_manual(values = getPalette(colourCount), name = "Breed") +  
  scale_y_continuous(labels = percent_format()) +
  ylab("Percent Increase") + 
  xlab("Breed") + 
  ggtitle(label = "Year Over Year (YOY) Increase in Breeds") + 
    coord_flip() + 
    theme(legend.position = "none") + 
  facet_wrap(~breed_level, scales = "free_y")

Dog Names

a = seattle_pet_licenses %>% 
  filter(species == "Dog",
         !is.na(animal_s_name)) %>% 
  select(animal_s_name, license_issue_date) %>% 
  mutate(animal_s_name = if_else(is.na(animal_s_name), "No Name", animal_s_name) ,
         license_issue_date = as.Date(license_issue_date),
         year = lubridate::year(license_issue_date)) %>% 
  group_by(animal_s_name, year) %>% 
  count() %>% 
  arrange(year, desc(n)) %>% 
  ungroup() %>% 
  group_by(year) %>% 
  mutate(rank = row_number()) %>% 
  filter(rank <= 5, year >= 2010) %>%
  mutate(rev_rank = rank(-rank)) %>% 
  ungroup() 
colourCount = length(unique(a$animal_s_name))
getPalette = colorRampPalette(brewer.pal(12, "Paired"))
ggplot(a, aes(x = year, y = reorder(rev_rank, rev_rank), fill = animal_s_name)) +
  geom_bar(stat = "identity", position = "stack", color = "white") + 
  scale_fill_manual(values = getPalette(colourCount), name = "Name") +  
  geom_text(stat = "identity", position = "stack", aes(label = rank), color = "black", size = 3, vjust = 1, hjust = 3) + 
  theme(axis.text.x=element_blank(),
        axis.ticks.x=element_blank()) +
  scale_x_continuous(breaks = seq(2010, 2016, 1)) + 
  coord_flip() +
  ylab("rank") + 
  
  ggtitle(label = "Top Five Seattle Dog Names by Year")

Breed Combos

Breed Heatmap

Which primary and secondary breek combinations are most popular?

top_breeds = rbind(
(seattle_pet_licenses %>% 
  filter(species == "Dog") %>% 
  select(primary_breed) %>% 
  group_by_all() %>% 
  count() %>% 
  arrange(desc(n)) %>% 
  head(20) %>% 
  rename("breed" = primary_breed)),
(seattle_pet_licenses %>% 
  filter(species == "Dog") %>% 
  select(secondary_breed) %>% 
  group_by_all() %>% 
  count() %>% 
  arrange(desc(n)) %>% 
  head(20) %>% 
  rename("breed" = secondary_breed)
)) %>% 
  group_by(breed) %>% 
  summarise(n = sum(n)) %>% 
  arrange(desc(n)) %>% 
  ungroup() %>% 
  mutate(breed = if_else(is.na(breed), "Unknown", breed))
plot = seattle_pet_licenses %>% 
  filter(species == "Dog", 
         primary_breed %in% top_breeds$breed
         ,secondary_breed %in% top_breeds$breed
         ) %>% 
  select(primary_breed, secondary_breed) %>% 
  mutate(secondary_breed = if_else(is.na(secondary_breed), primary_breed, secondary_breed)) %>% 
  group_by_all() %>% 
  count() %>% 
  arrange(desc(n)) %>% 
  #head(50) %>% 
  mutate(log = log(n + 1)) %>% 
  ggplot(
    aes(x = primary_breed,
        y = secondary_breed, 
        fill = log,
        text = paste(
          "Primary Breed:", primary_breed,
          "<br>Secondary Breed:", secondary_breed,
          "<br>Dogs:", comma(n)
        )
      )
    ) + 
  geom_tile() +
  theme(axis.text.x = element_text(angle = -30, hjust = 0)) + 
  scale_fill_gradientn(colours = blues9,
    na.value = "white",
    breaks=c(0,7),
    # # # #breaks = c(400, 700000),
    labels=c("0","1K"),
    limits=c(0,7),
    # # # #limits = c(400, 700000),
    name = "Dogs") +
  ylab("Secondary Breed") +
  xlab("Primary Breed") 
ggplotly(plot, tooltip = "text") %>% 
  layout(autosize = F, width = 800, height = 600)





Upset Plot (like a Venn Diagram, but better)




#This is the true code for this plot. Unfortunately, this package has a bug -- one that prints a giant blank space before the plot, which makes it less than optimal for publishing. My hack around this is to reproduce the plot using this code and save the image in my R Project file. From there I can just pull it in with html. A hack, yes, but one that works until the bug is fixed (according to their GitHub page, it's a WIP!). 
# upset_dog = rbind(
# (seattle_pet_licenses %>% 
#   filter(species == "Dog") %>% 
#    mutate(primary_breed = if_else(is.na(primary_breed), "Unknown", primary_breed)) %>% 
#   select(primary_breed, license_number) %>% 
#   rename("breed" = primary_breed)),
# (seattle_pet_licenses %>% 
#   filter(species == "Dog") %>% 
#    mutate(secondary_breed = if_else(is.na(secondary_breed), "Unknown", secondary_breed)) %>%  
#   select(secondary_breed, license_number) %>% 
#   rename("breed" = secondary_breed)
# )) %>% 
#   group_by(license_number, breed) %>%
#   count() %>% 
#   mutate(value = 1) %>%
#   select(-license_number, -n) %>% 
#   spread(breed, value, fill = 0) %>%
#   as.data.frame()
# 
# upset(upset_dog, 
#       order.by = "freq",
#       nsets = 15, 
#       mainbar.y.label = "Dog Breed Intersections", 
#       sets.x.label = "Total Number of Breed in Sample",
#       matrix.color = "#3288bd",
#       main.bar.color = "#f46d43",
#       sets.bar.color = "#3288bd", 
#       point.size = 2.5, line.size = 1, 
#       nintersects = 15,
#       shade.color = "#9e0142",
#       text.scale = c(1.5, 1.5, 1.5, 1.5, 1.5, 1.5),
#       group.by = "degree")

Breed Network

Breed Characteristics

What are the words used to describe different dog breeds? All breed descriptions were obtained through the Pet Breeds Characteristics public dataset.

We will score the top 20 breeds using the AFINN lexicon, which scores words by sentiment.

Before doing so, let’s inspect the data and make sure that we agree with the way the Lexicon is classifying words given our specific context.

AFINN <- get_sentiments("afinn")
pal <- rev(beyonce_palette(22, 100, type = "continuous"))
plot_c = barsentiment %>%
    dplyr::count(sentiment, word, n=n) %>%
    dplyr::ungroup() 
dog_breed_characteristics %>% 
  select(BreedName, Group1, Group2, Temperment) %>% 
  unnest_tokens(word, Temperment) %>% 
  inner_join(AFINN) %>% 
  select(word, score) %>% 
  unique() %>% 
  arrange(desc(score)) %>% 
  mutate(Sentiment = if_else(score > 0, "Positive", "Negative")) %>% 
  ggplot(aes(x = reorder(word, score), y = score, fill = Sentiment)) +
  geom_bar(stat = "identity") + 
  scale_fill_brewer(palette = "Set1") + 
  coord_flip() + 
  xlab("Word") + 
  ylab("Score")

A dog being alert does not seem like it would be a bad thing - let’s change that from a -1 to a +1.

Some dog breeds are described by more words than others. A histogram helps us visualize and normalize the data.

Now we’re ready to score our dogs by breed.

Top 20

dog_scores %>% 
  arrange(desc(total_score)) %>% 
  head(20) %>% 
  ggplot(aes(x = reorder(BreedName, total_score), y = total_score, fill = total_score)) +
  geom_bar(stat = "identity") + 
  scale_fill_gradientn(
    colours = pal, name = "Score") +
  coord_flip() + 
  xlab("Breed") + 
  ylab("Total Word Score")

Are dogs with more words scored higher? We can normalize by taking the mean word score.

dog_scores %>% 
  arrange(desc(mean_score), BreedName) %>%  
  head(20) %>% 
  ggplot(aes(x = reorder(BreedName, mean_score), y = mean_score, fill = total_score)) +
  geom_bar(stat = "identity") + 
  scale_fill_gradientn(
    colours = pal, name = "Total Score") +
  coord_flip() + 
  xlab("Breed") + 
  ylab("Mean Word Score")

Top 20 Breed Combos

By Group

Let’s hear it for the the Hounds!

group_scores = dog_words %>% 
  filter(!Group1 == "Southern") %>% 
  select(Group1, word) %>% 
  unique() %>% 
  inner_join(AFINN) %>% 
  group_by(Group1) %>% 
  summarise(avg_score = mean(score),
            score = sum(score)) %>% 
  drop_na()
group_scores %>% 
  arrange(desc(avg_score)) %>% 
  #head(20) %>% 
  ggplot(aes(x = reorder(Group1, avg_score), y = avg_score, fill = score)) +
  geom_bar(stat = "identity") + 
  scale_fill_gradientn(
    colours = pal, name = "Total Score") +
  coord_flip() + 
  xlab("Breed Group") + 
  ylab("Mean Score")

Breed Group 1 + Group 2

group_both = dog_words %>% 
  filter(!Group1 == "Southern") %>% 
  inner_join(AFINN) %>% 
  mutate(group_combo = paste(Group1, Group2, sep = " + ")) %>% 
  group_by(group_combo) %>% 
  summarise(
    avg_score = mean(score), 
    score = sum(score)
    ) %>% 
  #summarise(count = n_distinct(word)) %>% 
  drop_na()
group_both %>% 
  arrange(desc(avg_score)) %>% 
  #head(20) %>% 
  ggplot(aes(x = reorder(group_combo, avg_score), y = avg_score, fill = score)) +
  geom_bar(stat = "identity") + 
  scale_fill_gradientn(
    colours = pal, name = "Total Score") +
  coord_flip() + 
  xlab("Breed Group 1 + Breed Group 2") + 
  ylab("Mean Score")

Intelligence Ratings

Does intelligence correspond with price? No.

Pets by Zip YOY

seattle_pet_licenses %>% 
    drop_na() %>% 
  select(license_issue_date
         , species
         , zip_code
         , license_number) %>% 
  mutate(license_issue_date = as.Date(license_issue_date),
         year = lubridate::year(license_issue_date),
         zip_code = (as.factor(zip_code))
         ) %>%
  group_by(year, zip_code, species) %>% 
  summarise(new_pets = n_distinct(license_number)) %>% 
  ungroup() %>% 
  plot_ly(
  x = ~zip_code
  , y = ~new_pets
  , color = ~species
  , type = "bar"
  , legend_group = ~species
  , frame = ~year
  ) %>% 
  layout(
    barmode = "stack"
    , xaxis = list(title = "Zip Code")
    , yaxis = list(title = "New Pets")
  )
LS0tCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgY29kZV9mb2xkaW5nOiBoaWRlCiAgICB0b2M6IGZhbHNlCiAgICB0b2NfZmxvYXQ6IHRydWUKICAgIHRvY19kZXB0aDogNQogICAgbnVtYmVyX3NlY3Rpb25zOiBmYWxzZQogICAgCi0tLQo8c3R5bGUgdHlwZT0idGV4dC9jc3MiPgoKYm9keSB7CiAgZm9udC1zaXplOiAxMnB0OwogIGZvbnQtZmFtaWx5OiAiUm9ib3RvIiwgc2Fucy1zZXJpZjsKfQoKdGggewogICAgYmFja2dyb3VuZC1jb2xvcjogI2E2Y2VlMzsKICAgIGNvbG9yOiBibGFjazsKICAgIGZvbnQtc2l6ZTogMTBwdDsKICAgIGZvbnQtZmFtaWx5OiAiUm9ib3RvIiwgc2Fucy1zZXJpZjsKICAgIHRleHQtYWxpZ246IGxlZnQ7CiAgICA8IS0tIG1hcmdpbi1sZWZ0OiBhdXRvOyAtLT4KICAgIDwhLS0gbWFyZ2luLXJpZ2h0OiBhdXRvOyAtLT4KICAgIDwhLS0gcGFkZGluZy10b3A6IDI1cHg7IC0tPgogIH0KCnRkIHsgIC8qIFRhYmxlICAqLwogIGZvbnQtc2l6ZTogMTBwdDsKICA8IS0tIHRleHQtYWxpZ246IGNlbnRlcjsgLS0+CiAgZm9udC1mYW1pbHk6ICJSb2JvdG8iLCBzYW5zLXNlcmlmOwogIDwhLS0gcGFkZGluZy10b3A6IDI1cHg7IC0tPgp9CgpoMSB7CiAgZm9udC1zaXplOiAxNnB0OwogIGZvbnQtZmFtaWx5OiAiT3N3YWxkIiwgc2Fucy1zZXJpZjsKfQogIApoMiB7CiAgZm9udC1zaXplOiAxNHB0OwogIGZvbnQtZmFtaWx5OiAiT3N3YWxkIiwgc2Fucy1zZXJpZjsKICA8IS0tIGNvbG9yOiAjMWY3OGI0OyAtLT4KICBmb250LWZhbWlseTogIk9zd2FsZCIsIHNhbnMtc2VyaWY7Cn0KCmgzIHsKICBmb250LXNpemU6IDE0cHQ7CiAgZm9udC1mYW1pbHk6ICJPc3dhbGQiLCBzYW5zLXNlcmlmOwogIH0KICAKaDQgewogIGZvbnQtc2l6ZTogMTJwdDsKICBmb250LWZhbWlseTogIk9zd2FsZCIsIHNhbnMtc2VyaWY7Cn0KaDUgewogIGZvbnQtc2l6ZTogMTJwdDsKICBmb250LWZhbWlseTogIk9zd2FsZCIsIHNhbnMtc2VyaWY7Cn0KYSB7CiAgY29sb3I6ICMxZjc4YjQ7CiAgZm9udC1zaXplOiAxMnB0OwogIGZvbnQtZmFtaWx5OiAiT3N3YWxkIiwgc2Fucy1zZXJpZjsKfQoKCi5zaWRlbmF2IHsKICBoZWlnaHQ6IDEwMCU7CiAgd2lkdGg6IDIwMHB4OwogIHBvc2l0aW9uOiBmaXhlZDsKICB6LWluZGV4OiAxOwogIHRvcDogMDsKICBsZWZ0OiAwOwogIGJhY2tncm91bmQtY29sb3I6ICNhNmNlZTM7CiAgb3ZlcmZsb3cteDogaGlkZGVuOwogIHBhZGRpbmctdG9wOiAyMHB4Owp9Cgouc2lkZW5hdiBhIHsKICBwYWRkaW5nOiA2cHggOHB4IDZweCAxNnB4OwogIHRleHQtZGVjb3JhdGlvbjogbm9uZTsKICBmb250LXNpemU6IDE4cHQ7CiAgPCEtLSBmb250LXdlaWdodDogYm9sZGVyOyAtLT4KICBmb250LWZhbWlseTogIk9zd2FsZCIsIHNhbnMtc2VyaWY7CiAgY29sb3I6ICNGRkZGRkY7CiAgZGlzcGxheTogYmxvY2s7CiAgdGV4dC1hbGlnbjogY2VudGVyOwp9CgouY2VudGVyIHsKICBkaXNwbGF5OiBibG9jazsKICBtYXJnaW4tbGVmdDogYXV0bzsKICBtYXJnaW4tcmlnaHQ6IGF1dG87CiAgd2lkdGg6IDEwMCU7Cn0KCi5zaWRlbmF2IGE6aG92ZXIgewogIGNvbG9yOiAjZjFmMWYxOwp9CgoubWFpbiB7CiAgbWFyZ2luLWxlZnQ6IDIwMHB4OyAvKiBTYW1lIGFzIHRoZSB3aWR0aCBvZiB0aGUgc2lkZW5hdiAqLwoKfQoubWFpbi1jb250YWluZXIgewogIG1heC13aWR0aDogMTQwMHB4OwogIG1hcmdpbi1sZWZ0OiBhdXRvOwogIG1hcmdpbi1yaWdodDogYXV0bzsKICBwYWRkaW5nOiAyNXB4Cn0KICAvKnBhZGRpbmc6IDBweCA1cHg7ICovCn0KCkBtZWRpYSBzY3JlZW4gYW5kIChtYXgtaGVpZ2h0OiA0NTBweCkgewogIC5zaWRlbmF2IHtwYWRkaW5nLXRvcDogMTVweDt9CiAgLnNpZGVuYXYgYSB7Zm9udC1zaXplOiAxOHB4O30KfQo8L3N0eWxlPgoKPCEtLSBUSVRMRSBJTkZPICAtLT4KCjxkaXYgY2xhc3M9InNpZGVuYXYiPgogIDxhIGhyZWY9IiNhYm91dCI+U2VhdHRsZSBQZXRzPC9hPgogIDxhIGhyZWY9IiN0b3AiPiA8Zm9udCBmYWNlPSJSb2JvdG8iIHNpemU9IjIiIGNvbG9yPSAiIzFmNzhiNCI+IHRvcCBvZiBwYWdlIDwvZm9udD48L2E+CjwvZGl2PgoKPCEtLSBDT05URU5UIFNUQVJUUyBIRVJFICAtLT4KCjxkaXYgY2xhc3M9Im1haW4iPgo8ZGl2IGNsYXNzPSJib2R5Ij4KCiMjQXV0aG9yOiBfXzxmb250IGNvbG9yPSIjZTMxYTFjIj5KZXNzaWNhIE1hcng8L2ZvbnQ+X18KCiMjRGF0ZTogX188Zm9udCBjb2xvcj0iI2UzMWExYyI7IGZvbnQgc2l6ZT0iMyI+YHIgZm9ybWF0KFN5cy50aW1lKCksICIlZCAlQiAlWSIpYDwvZm9udD5fXwoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9Cgprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsIG1lc3NhZ2UgPSBGQUxTRSwgd2FybmluZyA9IEZBTFNFKQoKYGBgCgpgYGB7ciBwYWNrYWdlLCBtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmcgPSBGQUxTRX0KIyBsb2FkIHBhY2thZ2VzLCBpbnN0YWxsIGlmIG5lZWRlZApwYWNrYWdlcyA9IGMoCiAgICAgICJkcGx5ciIKICAgICwgImdncGxvdDIiCiAgICAsICJmb3JtYXR0YWJsZSIKICAgICwgInBsb3RseSIKICAgICwgIlJDb2xvckJyZXdlciIKICAgICwgInNjYWxlcyIKICAgICwgInN0cmluZ3IiCiAgICAsICJ0aWR5ciIKICAgICwgIkVsbWVSIgogICAgLCAiUkpEQkMiCiAgICAsICJrYWJsZUV4dHJhIgogICAgLCAid2VzYW5kZXJzb24iCiAgICAsICJyZXNoYXBlMiIKICAgICwgInJ0d2VldCIKICAgICwgInRpZHl0ZXh0IgogICAgLCAibHVicmlkYXRlIgogICAgLCAid29yZGNsb3VkIgogICAgLCAiZ2dwdWJyIgogICAgLCAiZ2d0aGVtZXMiCiAgICAsICJrbml0ckJvb3RzdHJhcCIKICAgICwgIkRUIgogICAgLCAiTWF0Y2hJdCIKICAgICwgImJleW9uY2UiCiAgICAsICJVcFNldFIiCiAgICAsICJnZ2FuaW1hdGUiCiAgICAsICJ3b3JkY2xvdWQyIgogICAgLCAid2lkeXIiCiAgICAsICJnZ3JhcGgiCiAgICAsICJpZ3JhcGgiCiAgICAsICJhb2QiCiAgICAsICJjb3JycGxvdCIKICAgICwgIlJPQ1IiCiAgICAsICJJbmZvcm1hdGlvblZhbHVlIgogICAgLCAiY2FyIgogICAgLCAiZ2xtbmV0IgogICAgLCAiY2FyZXQiCiAgICAsICJrZXJubGFiIgogICAgLCAicGRwIgogICAgLCAicnBhcnQucGxvdCIKICAgICwgInJwYXJ0IgogICAgLCAiZTEwNzEiCiAgICApCgpwYWNrYWdlLmNoZWNrIDwtIGxhcHBseShwYWNrYWdlcywgRlVOID0gZnVuY3Rpb24oeCkgewogIGlmICghcmVxdWlyZSh4LCBjaGFyYWN0ZXIub25seSA9IFRSVUUpKSB7CiAgICBpbnN0YWxsLnBhY2thZ2VzKHgsIGRlcGVuZGVuY2llcyA9IFRSVUUpCiAgICBsaWJyYXJ5KHgsIGNoYXJhY3Rlci5vbmx5ID0gVFJVRSkKICB9Cn0pCgoKb3B0aW9ucyhzY2lwZW49IDk5OSkKdGhlbWVfc2V0KHRoZW1lX21pbmltYWwoYmFzZV9zaXplID0gOSwgYmFzZV9mYW1pbHkgPSAiUm9ib3RvIikpCmBgYAoKYGBge3IgZnVuY3Rpb25zfQoKI2Z1bmN0aW9ucyEKCiNyb3VuZCAKcm91bmQudG8gPC0gZnVuY3Rpb24oeCwgYikgewogIHJvdW5kKHgvYikqYgp9Cgojb2RkcyB0byBwcm9iYWJpbGl0eQpvZGRzLnRvLnByb2IgPC0gZnVuY3Rpb24ob2RkcykgewogIG9kZHMvKDEgKyBvZGRzKSAKfQoKI2xvZyBvZGRzIHRvIHByb2JhYmlsaXR5IApsb2dpdDJwcm9iIDwtIGZ1bmN0aW9uKGxvZ2l0KXsKICBvZGRzIDwtIGV4cChsb2dpdCkKICBwcm9iIDwtIG9kZHMgLyAoMSArIG9kZHMpCiAgcmV0dXJuKHByb2IpCn0KCiNjb252ZXJ0IHRvIGEgcmFuZ2UKcmFuZ2UwMSA8LSBmdW5jdGlvbih4KXsKICAoeC1taW4oeCkpLyhtYXgoeCktbWluKHgpKQp9CgojZnVuY3Rpb24gdG8gZ2V0IHZlY3RvciBvZiBjb2xvciB2YWx1ZXMgZnJvbSBSQ29sb3JCcmV3ZXIKZ2V0X2hleF92YWx1ZXMgPC0gZnVuY3Rpb24ocGFsKSB7CglicmV3ZXIucGFsKGJyZXdlci5wYWwuaW5mb1twYWwsICJtYXhjb2xvcnMiXSwgcGFsKQp9CnBhaXJlZF9jb2xzIDwtIGdldF9oZXhfdmFsdWVzKHBhbCA9ICJQYWlyZWQiKQoKCmBgYAoKIyNUb3AgRG9nIEJyZWVkcyAKCmBgYHtyLCBmaWcuaGVpZ2h0PTUsIGZpZy53aWR0aD04fQoKYSA9IHNlYXR0bGVfcGV0X2xpY2Vuc2VzICU+JSAKICBmaWx0ZXIoc3BlY2llcyA9PSAiRG9nIikgJT4lIAogIHNlbGVjdChwcmltYXJ5X2JyZWVkLCBsaWNlbnNlX2lzc3VlX2RhdGUpICU+JSAKICBtdXRhdGUobGljZW5zZV9pc3N1ZV9kYXRlID0gYXMuRGF0ZShsaWNlbnNlX2lzc3VlX2RhdGUpLAogICAgICAgICB5ZWFyID0gbHVicmlkYXRlOjp5ZWFyKGxpY2Vuc2VfaXNzdWVfZGF0ZSkpICU+JSAKICBncm91cF9ieShwcmltYXJ5X2JyZWVkLCB5ZWFyKSAlPiUgCiAgY291bnQoKSAlPiUgCiAgYXJyYW5nZSh5ZWFyLCBkZXNjKG4pKSAlPiUgCiAgdW5ncm91cCgpICU+JSAKICBncm91cF9ieSh5ZWFyKSAlPiUgCiAgbXV0YXRlKHJhbmsgPSByb3dfbnVtYmVyKCkpICU+JSAKICBmaWx0ZXIocmFuayA8PSA1LCB5ZWFyID49IDIwMTApICU+JQogIG11dGF0ZShyZXZfcmFuayA9IHJhbmsoLXJhbmspKSAlPiUgCiAgdW5ncm91cCgpIAoKY29sb3VyQ291bnQgPSBsZW5ndGgodW5pcXVlKGEkcHJpbWFyeV9icmVlZCkpCmdldFBhbGV0dGUgPSBjb2xvclJhbXBQYWxldHRlKGJyZXdlci5wYWwoMTIsICJQYWlyZWQiKSkKCmdncGxvdChhLCBhZXMoeCA9IHllYXIsIHkgPSByZW9yZGVyKHJldl9yYW5rLCByZXZfcmFuayksIGZpbGwgPSBwcmltYXJ5X2JyZWVkKSkgKwogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBwb3NpdGlvbiA9ICJzdGFjayIsIGNvbG9yID0gIndoaXRlIikgKyAKICBnZW9tX3RleHQoc3RhdCA9ICJpZGVudGl0eSIsIHBvc2l0aW9uID0gInN0YWNrIiwgYWVzKGxhYmVsID0gcmFuayksIGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9IDMsIHZqdXN0ID0gMSwgaGp1c3QgPSAzKSArIAogIHRoZW1lKGF4aXMudGV4dC54PWVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRpY2tzLng9ZWxlbWVudF9ibGFuaygpKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgyMDEwLCAyMDE2LCAxKSkgKyAKICBjb29yZF9mbGlwKCkgKwogIHlsYWIoInJhbmsiKSArIAogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGdldFBhbGV0dGUoY29sb3VyQ291bnQpLCBuYW1lID0gIkJyZWVkIikgKyAgCiAgZ2d0aXRsZShsYWJlbCA9ICJUb3AgRml2ZSBTZWF0dGxlIERvZyBCcmVlZHMgYnkgWWVhciIpCgoKYGBgCgojI0JyZWVkIEFjY2VsZXJhdGlvbiAKCmBgYHtyfQoKYSA9IHNlYXR0bGVfcGV0X2xpY2Vuc2VzICU+JSAKICBmaWx0ZXIoc3BlY2llcyA9PSAiRG9nIikgJT4lIAogIHNlbGVjdChwcmltYXJ5X2JyZWVkLCBsaWNlbnNlX2lzc3VlX2RhdGUpICU+JSAKICBtdXRhdGUobGljZW5zZV9pc3N1ZV9kYXRlID0gYXMuRGF0ZShsaWNlbnNlX2lzc3VlX2RhdGUpLAogICAgICAgICB5ZWFyID0gbHVicmlkYXRlOjp5ZWFyKGxpY2Vuc2VfaXNzdWVfZGF0ZSkpICU+JSAKICBmaWx0ZXIoeWVhciA+PSAyMDE1KSAlPiUgCiAgZ3JvdXBfYnkocHJpbWFyeV9icmVlZCwgeWVhcikgJT4lIAogIGNvdW50KCkgJT4lCiAgc3ByZWFkKHllYXIsIG4pICU+JSAKICBkcm9wX25hKCkgJT4lIAogIG11dGF0ZShwY3RfaW5jcmVhc2UgPSBgMjAxNmAvYDIwMTVgLTEpICU+JSAKICBhcnJhbmdlKGRlc2MocGN0X2luY3JlYXNlKSkgJT4lIAogIGhlYWQoMjApICU+JSAKICB1bmdyb3VwKCkKCmIgPSBzZWF0dGxlX3BldF9saWNlbnNlcyAlPiUgCiAgZmlsdGVyKHNwZWNpZXMgPT0gIkRvZyIpICU+JSAKICBzZWxlY3Qoc2Vjb25kYXJ5X2JyZWVkLCBsaWNlbnNlX2lzc3VlX2RhdGUpICU+JSAKICBtdXRhdGUobGljZW5zZV9pc3N1ZV9kYXRlID0gYXMuRGF0ZShsaWNlbnNlX2lzc3VlX2RhdGUpLAogICAgICAgICB5ZWFyID0gbHVicmlkYXRlOjp5ZWFyKGxpY2Vuc2VfaXNzdWVfZGF0ZSkpICU+JSAKICBmaWx0ZXIoeWVhciA+PSAyMDE1KSAlPiUgCiAgZ3JvdXBfYnkoc2Vjb25kYXJ5X2JyZWVkLCB5ZWFyKSAlPiUgCiAgY291bnQoKSAlPiUKICBzcHJlYWQoeWVhciwgbikgJT4lIAogIGRyb3BfbmEoKSAlPiUgCiAgbXV0YXRlKHBjdF9pbmNyZWFzZSA9IGAyMDE2YC9gMjAxNWAtMSkgJT4lIAogIGFycmFuZ2UoZGVzYyhwY3RfaW5jcmVhc2UpKSAlPiUgCiAgaGVhZCgyMCkgJT4lIAogIHVuZ3JvdXAoKQoKYnJlZWRzID0gYSAlPiUgCiAgc2VsZWN0KHByaW1hcnlfYnJlZWQsIHBjdF9pbmNyZWFzZSkgJT4lIAogIHJlbmFtZSgiYnJlZWQiID0gcHJpbWFyeV9icmVlZCkgJT4lIAogIG11dGF0ZShicmVlZF9sZXZlbCA9ICJQcmltYXJ5IEJyZWVkIikgJT4lIAogIHJiaW5kKAogICAgYiAlPiUgCiAgc2VsZWN0KHNlY29uZGFyeV9icmVlZCwgcGN0X2luY3JlYXNlKSAlPiUgCiAgcmVuYW1lKCJicmVlZCIgPSBzZWNvbmRhcnlfYnJlZWQpICU+JSAKICBtdXRhdGUoYnJlZWRfbGV2ZWwgPSAiU2Vjb25kYXJ5IEJyZWVkIikKICApICU+JSAKICBncm91cF9ieShicmVlZF9sZXZlbCkgJT4lIAogIGFycmFuZ2UoYnJlZWRfbGV2ZWwsIGRlc2MocGN0X2luY3JlYXNlKSkgJT4lIAogIG11dGF0ZShyYW5rID0gcm93X251bWJlcigpKSAlPiUgCiAgdW5ncm91cCgpCiAgCmNvbG91ckNvdW50ID0gbGVuZ3RoKHVuaXF1ZShicmVlZHMkYnJlZWQpKQpnZXRQYWxldHRlID0gY29sb3JSYW1wUGFsZXR0ZShicmV3ZXIucGFsKDEyLCAiUGFpcmVkIikpCgpicmVlZHMgJT4lIAogIGdncGxvdChhZXMoeCA9IHJlb3JkZXIoYnJlZWQsIC1yYW5rKSwgeSA9IHBjdF9pbmNyZWFzZSwgZmlsbCA9IGJyZWVkKSkgKwogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiKSArIAogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGdldFBhbGV0dGUoY29sb3VyQ291bnQpLCBuYW1lID0gIkJyZWVkIikgKyAgCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IHBlcmNlbnRfZm9ybWF0KCkpICsKICB5bGFiKCJQZXJjZW50IEluY3JlYXNlIikgKyAKICB4bGFiKCJCcmVlZCIpICsgCiAgZ2d0aXRsZShsYWJlbCA9ICJZZWFyIE92ZXIgWWVhciAoWU9ZKSBJbmNyZWFzZSBpbiBCcmVlZHMiKSArIAogICAgY29vcmRfZmxpcCgpICsgCiAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsgCiAgZmFjZXRfd3JhcCh+YnJlZWRfbGV2ZWwsIHNjYWxlcyA9ICJmcmVlX3kiKQoKCmBgYAoKIyNEb2cgTmFtZXMKCmBgYHtyLCBmaWcuaGVpZ2h0PTUsIGZpZy53aWR0aD04fQoKYSA9IHNlYXR0bGVfcGV0X2xpY2Vuc2VzICU+JSAKICBmaWx0ZXIoc3BlY2llcyA9PSAiRG9nIiwKICAgICAgICAgIWlzLm5hKGFuaW1hbF9zX25hbWUpKSAlPiUgCiAgc2VsZWN0KGFuaW1hbF9zX25hbWUsIGxpY2Vuc2VfaXNzdWVfZGF0ZSkgJT4lIAogIG11dGF0ZShhbmltYWxfc19uYW1lID0gaWZfZWxzZShpcy5uYShhbmltYWxfc19uYW1lKSwgIk5vIE5hbWUiLCBhbmltYWxfc19uYW1lKSAsCiAgICAgICAgIGxpY2Vuc2VfaXNzdWVfZGF0ZSA9IGFzLkRhdGUobGljZW5zZV9pc3N1ZV9kYXRlKSwKICAgICAgICAgeWVhciA9IGx1YnJpZGF0ZTo6eWVhcihsaWNlbnNlX2lzc3VlX2RhdGUpKSAlPiUgCiAgZ3JvdXBfYnkoYW5pbWFsX3NfbmFtZSwgeWVhcikgJT4lIAogIGNvdW50KCkgJT4lIAogIGFycmFuZ2UoeWVhciwgZGVzYyhuKSkgJT4lIAogIHVuZ3JvdXAoKSAlPiUgCiAgZ3JvdXBfYnkoeWVhcikgJT4lIAogIG11dGF0ZShyYW5rID0gcm93X251bWJlcigpKSAlPiUgCiAgZmlsdGVyKHJhbmsgPD0gNSwgeWVhciA+PSAyMDEwKSAlPiUKICBtdXRhdGUocmV2X3JhbmsgPSByYW5rKC1yYW5rKSkgJT4lIAogIHVuZ3JvdXAoKSAKCmNvbG91ckNvdW50ID0gbGVuZ3RoKHVuaXF1ZShhJGFuaW1hbF9zX25hbWUpKQpnZXRQYWxldHRlID0gY29sb3JSYW1wUGFsZXR0ZShicmV3ZXIucGFsKDEyLCAiUGFpcmVkIikpCgpnZ3Bsb3QoYSwgYWVzKHggPSB5ZWFyLCB5ID0gcmVvcmRlcihyZXZfcmFuaywgcmV2X3JhbmspLCBmaWxsID0gYW5pbWFsX3NfbmFtZSkpICsKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgcG9zaXRpb24gPSAic3RhY2siLCBjb2xvciA9ICJ3aGl0ZSIpICsgCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gZ2V0UGFsZXR0ZShjb2xvdXJDb3VudCksIG5hbWUgPSAiTmFtZSIpICsgIAogIGdlb21fdGV4dChzdGF0ID0gImlkZW50aXR5IiwgcG9zaXRpb24gPSAic3RhY2siLCBhZXMobGFiZWwgPSByYW5rKSwgY29sb3IgPSAiYmxhY2siLCBzaXplID0gMywgdmp1c3QgPSAxLCBoanVzdCA9IDMpICsgCiAgdGhlbWUoYXhpcy50ZXh0Lng9ZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGlja3MueD1lbGVtZW50X2JsYW5rKCkpICsKICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDIwMTAsIDIwMTYsIDEpKSArIAogIGNvb3JkX2ZsaXAoKSArCiAgeWxhYigicmFuayIpICsgCiAgCiAgZ2d0aXRsZShsYWJlbCA9ICJUb3AgRml2ZSBTZWF0dGxlIERvZyBOYW1lcyBieSBZZWFyIikKCgpgYGAKCiMjQnJlZWQgQ29tYm9zIHsudGFic2V0fQoKIyMjQnJlZWQgSGVhdG1hcApXaGljaCBwcmltYXJ5IGFuZCBzZWNvbmRhcnkgYnJlZWsgY29tYmluYXRpb25zIGFyZSBtb3N0IHBvcHVsYXI/IAoKYGBge3J9Cgp0b3BfYnJlZWRzID0gcmJpbmQoCihzZWF0dGxlX3BldF9saWNlbnNlcyAlPiUgCiAgZmlsdGVyKHNwZWNpZXMgPT0gIkRvZyIpICU+JSAKICBzZWxlY3QocHJpbWFyeV9icmVlZCkgJT4lIAogIGdyb3VwX2J5X2FsbCgpICU+JSAKICBjb3VudCgpICU+JSAKICBhcnJhbmdlKGRlc2MobikpICU+JSAKICBoZWFkKDIwKSAlPiUgCiAgcmVuYW1lKCJicmVlZCIgPSBwcmltYXJ5X2JyZWVkKSksCihzZWF0dGxlX3BldF9saWNlbnNlcyAlPiUgCiAgZmlsdGVyKHNwZWNpZXMgPT0gIkRvZyIpICU+JSAKICBzZWxlY3Qoc2Vjb25kYXJ5X2JyZWVkKSAlPiUgCiAgZ3JvdXBfYnlfYWxsKCkgJT4lIAogIGNvdW50KCkgJT4lIAogIGFycmFuZ2UoZGVzYyhuKSkgJT4lIAogIGhlYWQoMjApICU+JSAKICByZW5hbWUoImJyZWVkIiA9IHNlY29uZGFyeV9icmVlZCkKKSkgJT4lIAogIGdyb3VwX2J5KGJyZWVkKSAlPiUgCiAgc3VtbWFyaXNlKG4gPSBzdW0obikpICU+JSAKICBhcnJhbmdlKGRlc2MobikpICU+JSAKICB1bmdyb3VwKCkgJT4lIAogIG11dGF0ZShicmVlZCA9IGlmX2Vsc2UoaXMubmEoYnJlZWQpLCAiVW5rbm93biIsIGJyZWVkKSkKCnBsb3QgPSBzZWF0dGxlX3BldF9saWNlbnNlcyAlPiUgCiAgZmlsdGVyKHNwZWNpZXMgPT0gIkRvZyIsIAogICAgICAgICBwcmltYXJ5X2JyZWVkICVpbiUgdG9wX2JyZWVkcyRicmVlZAogICAgICAgICAsc2Vjb25kYXJ5X2JyZWVkICVpbiUgdG9wX2JyZWVkcyRicmVlZAogICAgICAgICApICU+JSAKICBzZWxlY3QocHJpbWFyeV9icmVlZCwgc2Vjb25kYXJ5X2JyZWVkKSAlPiUgCiAgbXV0YXRlKHNlY29uZGFyeV9icmVlZCA9IGlmX2Vsc2UoaXMubmEoc2Vjb25kYXJ5X2JyZWVkKSwgcHJpbWFyeV9icmVlZCwgc2Vjb25kYXJ5X2JyZWVkKSkgJT4lIAogIGdyb3VwX2J5X2FsbCgpICU+JSAKICBjb3VudCgpICU+JSAKICBhcnJhbmdlKGRlc2MobikpICU+JSAKICAjaGVhZCg1MCkgJT4lIAogIG11dGF0ZShsb2cgPSBsb2cobiArIDEpKSAlPiUgCiAgZ2dwbG90KAogICAgYWVzKHggPSBwcmltYXJ5X2JyZWVkLAogICAgICAgIHkgPSBzZWNvbmRhcnlfYnJlZWQsIAogICAgICAgIGZpbGwgPSBsb2csCiAgICAgICAgdGV4dCA9IHBhc3RlKAogICAgICAgICAgIlByaW1hcnkgQnJlZWQ6IiwgcHJpbWFyeV9icmVlZCwKICAgICAgICAgICI8YnI+U2Vjb25kYXJ5IEJyZWVkOiIsIHNlY29uZGFyeV9icmVlZCwKICAgICAgICAgICI8YnI+RG9nczoiLCBjb21tYShuKQogICAgICAgICkKICAgICAgKQogICAgKSArIAogIGdlb21fdGlsZSgpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IC0zMCwgaGp1c3QgPSAwKSkgKyAKICBzY2FsZV9maWxsX2dyYWRpZW50bihjb2xvdXJzID0gYmx1ZXM5LAogICAgbmEudmFsdWUgPSAid2hpdGUiLAogICAgYnJlYWtzPWMoMCw3KSwKICAgICMgIyAjICNicmVha3MgPSBjKDQwMCwgNzAwMDAwKSwKICAgIGxhYmVscz1jKCIwIiwiMUsiKSwKICAgIGxpbWl0cz1jKDAsNyksCiAgICAjICMgIyAjbGltaXRzID0gYyg0MDAsIDcwMDAwMCksCiAgICBuYW1lID0gIkRvZ3MiKSArCiAgeWxhYigiU2Vjb25kYXJ5IEJyZWVkIikgKwogIHhsYWIoIlByaW1hcnkgQnJlZWQiKSAKCmdncGxvdGx5KHBsb3QsIHRvb2x0aXAgPSAidGV4dCIpICU+JSAKICBsYXlvdXQoYXV0b3NpemUgPSBGLCB3aWR0aCA9IDgwMCwgaGVpZ2h0ID0gNjAwKQoKYGBgCjxicj4KPGJyPgo8YnI+Cjxicj4gCgojIyNVcHNldCBQbG90IChsaWtlIGEgVmVubiBEaWFncmFtLCBidXQgYmV0dGVyKQo8YnI+CjxpbWcgc3JjPSJzZWF0dGxlX2RvZ3MucG5nIiBhbHQ9IiIgd2lkdGg9IjgwMCIvPgo8YnI+CgotLS0KCgpgYGB7cn0KCiNUaGlzIGlzIHRoZSB0cnVlIGNvZGUgZm9yIHRoaXMgcGxvdC4gVW5mb3J0dW5hdGVseSwgdGhpcyBwYWNrYWdlIGhhcyBhIGJ1ZyAtLSBvbmUgdGhhdCBwcmludHMgYSBnaWFudCBibGFuayBzcGFjZSBiZWZvcmUgdGhlIHBsb3QsIHdoaWNoIG1ha2VzIGl0IGxlc3MgdGhhbiBvcHRpbWFsIGZvciBwdWJsaXNoaW5nLiBNeSBoYWNrIGFyb3VuZCB0aGlzIGlzIHRvIHJlcHJvZHVjZSB0aGUgcGxvdCB1c2luZyB0aGlzIGNvZGUgYW5kIHNhdmUgdGhlIGltYWdlIGluIG15IFIgUHJvamVjdCBmaWxlLiBGcm9tIHRoZXJlIEkgY2FuIGp1c3QgcHVsbCBpdCBpbiB3aXRoIGh0bWwuIEEgaGFjaywgeWVzLCBidXQgb25lIHRoYXQgd29ya3MgdW50aWwgdGhlIGJ1ZyBpcyBmaXhlZCAoYWNjb3JkaW5nIHRvIHRoZWlyIEdpdEh1YiBwYWdlLCBpdCdzIGEgV0lQISkuIAoKIyB1cHNldF9kb2cgPSByYmluZCgKIyAoc2VhdHRsZV9wZXRfbGljZW5zZXMgJT4lIAojICAgZmlsdGVyKHNwZWNpZXMgPT0gIkRvZyIpICU+JSAKIyAgICBtdXRhdGUocHJpbWFyeV9icmVlZCA9IGlmX2Vsc2UoaXMubmEocHJpbWFyeV9icmVlZCksICJVbmtub3duIiwgcHJpbWFyeV9icmVlZCkpICU+JSAKIyAgIHNlbGVjdChwcmltYXJ5X2JyZWVkLCBsaWNlbnNlX251bWJlcikgJT4lIAojICAgcmVuYW1lKCJicmVlZCIgPSBwcmltYXJ5X2JyZWVkKSksCiMgKHNlYXR0bGVfcGV0X2xpY2Vuc2VzICU+JSAKIyAgIGZpbHRlcihzcGVjaWVzID09ICJEb2ciKSAlPiUgCiMgICAgbXV0YXRlKHNlY29uZGFyeV9icmVlZCA9IGlmX2Vsc2UoaXMubmEoc2Vjb25kYXJ5X2JyZWVkKSwgIlVua25vd24iLCBzZWNvbmRhcnlfYnJlZWQpKSAlPiUgIAojICAgc2VsZWN0KHNlY29uZGFyeV9icmVlZCwgbGljZW5zZV9udW1iZXIpICU+JSAKIyAgIHJlbmFtZSgiYnJlZWQiID0gc2Vjb25kYXJ5X2JyZWVkKQojICkpICU+JSAKIyAgIGdyb3VwX2J5KGxpY2Vuc2VfbnVtYmVyLCBicmVlZCkgJT4lCiMgICBjb3VudCgpICU+JSAKIyAgIG11dGF0ZSh2YWx1ZSA9IDEpICU+JQojICAgc2VsZWN0KC1saWNlbnNlX251bWJlciwgLW4pICU+JSAKIyAgIHNwcmVhZChicmVlZCwgdmFsdWUsIGZpbGwgPSAwKSAlPiUKIyAgIGFzLmRhdGEuZnJhbWUoKQojIAojIHVwc2V0KHVwc2V0X2RvZywgCiMgICAgICAgb3JkZXIuYnkgPSAiZnJlcSIsCiMgICAgICAgbnNldHMgPSAxNSwgCiMgICAgICAgbWFpbmJhci55LmxhYmVsID0gIkRvZyBCcmVlZCBJbnRlcnNlY3Rpb25zIiwgCiMgICAgICAgc2V0cy54LmxhYmVsID0gIlRvdGFsIE51bWJlciBvZiBCcmVlZCBpbiBTYW1wbGUiLAojICAgICAgIG1hdHJpeC5jb2xvciA9ICIjMzI4OGJkIiwKIyAgICAgICBtYWluLmJhci5jb2xvciA9ICIjZjQ2ZDQzIiwKIyAgICAgICBzZXRzLmJhci5jb2xvciA9ICIjMzI4OGJkIiwgCiMgICAgICAgcG9pbnQuc2l6ZSA9IDIuNSwgbGluZS5zaXplID0gMSwgCiMgICAgICAgbmludGVyc2VjdHMgPSAxNSwKIyAgICAgICBzaGFkZS5jb2xvciA9ICIjOWUwMTQyIiwKIyAgICAgICB0ZXh0LnNjYWxlID0gYygxLjUsIDEuNSwgMS41LCAxLjUsIDEuNSwgMS41KSwKIyAgICAgICBncm91cC5ieSA9ICJkZWdyZWUiKQoKCgpgYGAKCgojIyNCcmVlZCBOZXR3b3JrCgpgYGB7ciBlY2hvID0gRkFMU0UsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIGZpZy53aWR0aD05fQoKYnJlZWRfcGFpcnMgPSBzZWF0dGxlX3BldF9saWNlbnNlcyAlPiUgCiAgZmlsdGVyKHNwZWNpZXMgPT0gIkRvZyIpICU+JSAKICBzZWxlY3QocHJpbWFyeV9icmVlZCwgc2Vjb25kYXJ5X2JyZWVkKSAlPiUgCiAgcGFpcndpc2VfY291bnQocHJpbWFyeV9icmVlZCwgc2Vjb25kYXJ5X2JyZWVkLCBzb3J0ID0gVFJVRSkKCiMgbWFrZSBhbiBpbmRleCBlZy4gZXZlcnkgMm5kCmluZCA8LSBzZXEoMSwgbnJvdyhicmVlZF9wYWlycyksIGJ5PTIpCgojdGhpcyB3b3VsZCBleGNsdWRlIHRoZSBpbmQgcm93CmJyZWVkX3BhaXJzIDwtIGJyZWVkX3BhaXJzWy1pbmQsIF0gJT4lIAogIHJlbmFtZSgiZnJvbSIgPSBpdGVtMSwKICAgICAgICAgInRvIiA9IGl0ZW0yKQoKZWRnZXMgPC0gYnJlZWRfcGFpcnMgJT4lIAogIHJlbmFtZSgid2VpZ2h0IiA9IG4pICU+JSAKICBoZWFkKDEwMCkKbm9kZXMgPC0gZWRnZXMgJT4lIGdhdGhlcihpdGVtLCBpZCwgZnJvbSwgdG8pICU+JQogIGdyb3VwX2J5KGlkKSAlPiUgCiAgZHBseXI6OnN1bW1hcmlzZSh3ZWlnaHQgPSBzdW0od2VpZ2h0KSkgJT4lIAogIGFycmFuZ2UoZGVzYyh3ZWlnaHQpKSAlPiUgCiAgI211dGF0ZShpdGVtX3R5cGUgPSBpZl9lbHNlKGlkICVpbiUgYnJhbmRfdG90JGJyYW5kX25hbWUsICJEU05SIiwgIk5PTi1EU05SIikpICU+JSAKICB1bmdyb3VwKCkKCiMgYWRkaW5nIGRpZmZlcmVudCBmZWF0dXJlcyBpbnRvIHRoZSBub2Rlcwpub2RlcyRmb250LnNpemUgPSByZXNjYWxlKG5vZGVzJHdlaWdodCwgdG8gPSBjKDcwLCAyMDApKS9zYXBwbHkobm9kZXMkaWQsIGZ1bmN0aW9uKHgpIG5jaGFyKGFzLmNoYXJhY3Rlcih4KSkpCm5vZGVzJHNoYXBlID0gJ2NpcmNsZScKIyBhZGRpbmcgZGlmZmVyZW50IGZlYXR1cmVzIGludG8gdGhlIGVkZ2VzCmVkZ2VzJHdpZHRoID0gcmVzY2FsZShlZGdlcyR3ZWlnaHQsIHRvID0gYyg0LCAyNSkpCiNlZGdlcyRsZW5ndGggPSByZXNjYWxlKGVkZ2VzJHdpZHRoLCB0byA9IGMoMzAwLCA0MDApKQojIHVzZSB0aGUgZnJvbSBub2RlcyBjb2xvciBhcyB0aGUgY29sb3Igb2YgdGhlIGVkZ2VzCiNlZGdlcyRjb2xvciA9IG5vZGVzW1snY29sb3InXV1bZWRnZXNbWydmcm9tJ11dXQplZGdlcyRjb2xvciA9ICIjMWY3OGI0IgojIGNyZWF0ZSBhIGlncmFwaCBvYmplY3QgZm9yIGZ1dHVyZSBwbG90dGluZwpuZXQgPSBncmFwaF9mcm9tX2RhdGFfZnJhbWUoZCA9IGVkZ2VzLCB2ZXJ0aWNlcyA9IG5vZGVzKQoKbGlicmFyeSh2aXNOZXR3b3JrKQojIGludGVyYWN0aXZlIHBsb3Qgb2YgdGhlIGZlYXR1cmVzCnZpc05ldHdvcmsobm9kZXMsIGVkZ2VzKSU+JQogIHZpc0VkZ2VzKHNtb290aCA9IFRSVUUsIGRhc2hlcyA9IFRSVUUpICU+JQogIHZpc05vZGVzKHNoYWRvdyA9IFRSVUUsIGNvbG9yPWxpc3QoYmFja2dyb3VuZD0icGluayIsIGhpZ2hsaWdodD0iI2E2Y2VlMyIsIGJvcmRlcj0iYmxhY2siKSkgJT4lCiAgdmlzSWdyYXBoTGF5b3V0KGxheW91dCA9ICJsYXlvdXRfbmljZWx5IikgJT4lCiAgdmlzT3B0aW9ucyhoaWdobGlnaHROZWFyZXN0ID0gbGlzdChlbmFibGVkPVRSVUUsIGRlZ3JlZSA9IGxpc3QoZnJvbSA9IDEsIHRvID0gMSksIGxhYmVsT25seT1UUlVFKSkgJT4lIAogIHZpc0xheW91dChyYW5kb21TZWVkID0gMTIzNCkgCgpgYGAKCiMjQnJlZWQgQ2hhcmFjdGVyaXN0aWNzIHsudGFic2V0fQpXaGF0IGFyZSB0aGUgd29yZHMgdXNlZCB0byBkZXNjcmliZSBkaWZmZXJlbnQgZG9nIGJyZWVkcz8gQWxsIGJyZWVkIGRlc2NyaXB0aW9ucyB3ZXJlIG9idGFpbmVkIHRocm91Z2ggdGhlIDxhIGhyZWY9Imh0dHBzOi8vd3d3LmthZ2dsZS5jb20vcnR1cmxleS9wZXQtYnJlZWQtY2hhcmFjdGVyaXN0aWNzIiB0YXJnZXQ9Il9ibGFuayI+UGV0IEJyZWVkcyBDaGFyYWN0ZXJpc3RpY3M8L2E+IHB1YmxpYyBkYXRhc2V0LgoKYGBge3J9CgphID0gZG9nX3dvcmRzICU+JSAKICBncm91cF9ieSh3b3JkKSAlPiUgCiAgY291bnQoKSAlPiUgCiAgZHJvcF9uYSgpICU+JSAKICBhcnJhbmdlKGRlc2MobikpCgp3b3JkY2xvdWQoYSR3b3JkLCBhJG4sICwgMiwgLEZBTFNFLCAsIC4xNSwgcmV2KGdldF9oZXhfdmFsdWVzKCJQYWlyZWQiKSkpCiN3b3JkY2xvdWQyKGEsIHNpemUgPSAuNSwgZmlnUGF0aCA9ICJkb2dfb3V0bGluZS5wbmciKQojbGV0dGVyQ2xvdWQoYSwgd29yZCA9ICJEIikKCgpgYGAKCldlIHdpbGwgc2NvcmUgdGhlIHRvcCAyMCBicmVlZHMgdXNpbmcgdGhlIEFGSU5OIGxleGljb24sIHdoaWNoIHNjb3JlcyB3b3JkcyBieSBzZW50aW1lbnQuIAo8YnI+Cjxicj4KCkJlZm9yZSBkb2luZyBzbywgbGV0J3MgaW5zcGVjdCB0aGUgZGF0YSBhbmQgbWFrZSBzdXJlIHRoYXQgd2UgYWdyZWUgd2l0aCB0aGUgd2F5IHRoZSBMZXhpY29uIGlzIGNsYXNzaWZ5aW5nIHdvcmRzIGdpdmVuIG91ciBzcGVjaWZpYyBjb250ZXh0LiAKCmBgYHtyfQoKQUZJTk4gPC0gZ2V0X3NlbnRpbWVudHMoImFmaW5uIikKCnBhbCA8LSByZXYoYmV5b25jZV9wYWxldHRlKDIyLCAxMDAsIHR5cGUgPSAiY29udGludW91cyIpKQoKcGxvdF9jID0gYmFyc2VudGltZW50ICU+JQogICAgZHBseXI6OmNvdW50KHNlbnRpbWVudCwgd29yZCwgbj1uKSAlPiUKICAgIGRwbHlyOjp1bmdyb3VwKCkgCgpkb2dfYnJlZWRfY2hhcmFjdGVyaXN0aWNzICU+JSAKICBzZWxlY3QoQnJlZWROYW1lLCBHcm91cDEsIEdyb3VwMiwgVGVtcGVybWVudCkgJT4lIAogIHVubmVzdF90b2tlbnMod29yZCwgVGVtcGVybWVudCkgJT4lIAogIGlubmVyX2pvaW4oQUZJTk4pICU+JSAKICBzZWxlY3Qod29yZCwgc2NvcmUpICU+JSAKICB1bmlxdWUoKSAlPiUgCiAgYXJyYW5nZShkZXNjKHNjb3JlKSkgJT4lIAogIG11dGF0ZShTZW50aW1lbnQgPSBpZl9lbHNlKHNjb3JlID4gMCwgIlBvc2l0aXZlIiwgIk5lZ2F0aXZlIikpICU+JSAKICBnZ3Bsb3QoYWVzKHggPSByZW9yZGVyKHdvcmQsIHNjb3JlKSwgeSA9IHNjb3JlLCBmaWxsID0gU2VudGltZW50KSkgKwogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiKSArIAogIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGUgPSAiU2V0MSIpICsgCiAgY29vcmRfZmxpcCgpICsgCiAgeGxhYigiV29yZCIpICsgCiAgeWxhYigiU2NvcmUiKQoKYGBgCgpBIGRvZyBiZWluZyBfYWxlcnRfIGRvZXMgbm90IHNlZW0gbGlrZSBpdCB3b3VsZCBiZSBhIGJhZCB0aGluZyAtIGxldCdzIGNoYW5nZSB0aGF0IGZyb20gYSAtMSB0byBhICsxLiAKClNvbWUgZG9nIGJyZWVkcyBhcmUgZGVzY3JpYmVkIGJ5IG1vcmUgd29yZHMgdGhhbiBvdGhlcnMuIEEgaGlzdG9ncmFtIGhlbHBzIHVzIHZpc3VhbGl6ZSBhbmQgbm9ybWFsaXplIHRoZSBkYXRhLiAKCmBgYHtyfQoKQUZJTk4gPSBBRklOTiAlPiUgCiAgbXV0YXRlKHNjb3JlID0gaWZlbHNlKHdvcmQgPT0gImFsZXJ0IiwgMSwgc2NvcmUpKQoKZG9nX3dvcmRzID0gZG9nX2JyZWVkX2NoYXJhY3RlcmlzdGljcyAlPiUgCiAgc2VsZWN0KEJyZWVkTmFtZSwgR3JvdXAxLCBHcm91cDIsIFRlbXBlcm1lbnQpICU+JSAKICB1bm5lc3RfdG9rZW5zKHdvcmQsIFRlbXBlcm1lbnQpCgpkb2dfc2NvcmVzID0gZG9nX3dvcmRzICU+JSAKICBpbm5lcl9qb2luKEFGSU5OKSAlPiUgCiAgZ3JvdXBfYnkoQnJlZWROYW1lKSAlPiUgCiAgc3VtbWFyaXNlKHRvdGFsX3Njb3JlID0gc3VtKHNjb3JlKSwKICAgICAgICAgICAgbWVkX3Njb3JlID0gbWVkaWFuKHNjb3JlKSwKICAgICAgICAgICAgdG90YWxfd29yZHMgPSBuX2Rpc3RpbmN0KHdvcmQpKSAlPiUgCiAgbXV0YXRlKG1lYW5fc2NvcmUgPSB0b3RhbF9zY29yZS90b3RhbF93b3JkcykKCmRvZ19zY29yZXMgJT4lIAogIGdhdGhlcihLUEksIHZhbHVlLCB0b3RhbF9zY29yZSwgdG90YWxfd29yZHMsIG1lYW5fc2NvcmUsIG1lZF9zY29yZSkgJT4lIAogIGdncGxvdChhZXMoeCA9IHZhbHVlLCBmaWxsID0gS1BJKSkgKyAKICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDAuNSkgKyAKICBmYWNldF93cmFwKH5LUEksIHNjYWxlcyA9ICJmcmVlX3giKSArIAogIHhsYWIoIiIpICsgCiAgeWxhYigiRnJlcXVlbmN5IikgKyAKICBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlID0gIlBhaXJlZCIpCgpgYGAKCk5vdyB3ZSdyZSByZWFkeSB0byBzY29yZSBvdXIgZG9ncyBieSBicmVlZC4gCgoKIyMjVG9wIDIwCgpgYGB7cn0KCmRvZ19zY29yZXMgJT4lIAogIGFycmFuZ2UoZGVzYyh0b3RhbF9zY29yZSkpICU+JSAKICBoZWFkKDIwKSAlPiUgCiAgZ2dwbG90KGFlcyh4ID0gcmVvcmRlcihCcmVlZE5hbWUsIHRvdGFsX3Njb3JlKSwgeSA9IHRvdGFsX3Njb3JlLCBmaWxsID0gdG90YWxfc2NvcmUpKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpICsgCiAgc2NhbGVfZmlsbF9ncmFkaWVudG4oCiAgICBjb2xvdXJzID0gcGFsLCBuYW1lID0gIlNjb3JlIikgKwogIGNvb3JkX2ZsaXAoKSArIAogIHhsYWIoIkJyZWVkIikgKyAKICB5bGFiKCJUb3RhbCBXb3JkIFNjb3JlIikKCmBgYAoKQXJlIGRvZ3Mgd2l0aCBtb3JlIHdvcmRzIHNjb3JlZCBoaWdoZXI/IFdlIGNhbiBub3JtYWxpemUgYnkgdGFraW5nIHRoZSBtZWFuIHdvcmQgc2NvcmUuIAoKYGBge3J9Cgpkb2dfc2NvcmVzICU+JSAKICBhcnJhbmdlKGRlc2MobWVhbl9zY29yZSksIEJyZWVkTmFtZSkgJT4lICAKICBoZWFkKDIwKSAlPiUgCiAgZ2dwbG90KGFlcyh4ID0gcmVvcmRlcihCcmVlZE5hbWUsIG1lYW5fc2NvcmUpLCB5ID0gbWVhbl9zY29yZSwgZmlsbCA9IHRvdGFsX3Njb3JlKSkgKwogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiKSArIAogIHNjYWxlX2ZpbGxfZ3JhZGllbnRuKAogICAgY29sb3VycyA9IHBhbCwgbmFtZSA9ICJUb3RhbCBTY29yZSIpICsKICBjb29yZF9mbGlwKCkgKyAKICB4bGFiKCJCcmVlZCIpICsgCiAgeWxhYigiTWVhbiBXb3JkIFNjb3JlIikKCmBgYAoKCjwhLS0gIyMjQm90dG9tIDIwIC0tPgoKPCEtLSBgYGB7cn0gLS0+Cgo8IS0tIHBhbCA8LSAoYmV5b25jZV9wYWxldHRlKDQ4LCAxMDAsIHR5cGUgPSAiY29udGludW91cyIpKSAtLT4KCjwhLS0gZG9nX3Njb3JlcyAlPiUgIC0tPgo8IS0tICAgYXJyYW5nZShtZWFuX3Njb3JlKSAlPiUgIC0tPgo8IS0tICAgaGVhZCgyMCkgJT4lICAtLT4KPCEtLSAgIGdncGxvdChhZXMoeCA9IHJlb3JkZXIoQnJlZWROYW1lLCBzY29yZSksIHkgPSBzY29yZSwgZmlsbCA9IHNjb3JlKSkgKyAtLT4KPCEtLSAgIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiKSArICAtLT4KPCEtLSAgIHNjYWxlX2ZpbGxfZ3JhZGllbnRuKCAtLT4KPCEtLSAgICAgY29sb3VycyA9IHBhbCwgbmFtZSA9ICJTY29yZSIpICsgLS0+CjwhLS0gICBjb29yZF9mbGlwKCkgKyAgLS0+CjwhLS0gICB4bGFiKCJCcmVlZCIpICsgIC0tPgo8IS0tICAgeWxhYigiU2NvcmUiKSAtLT4KCjwhLS0gYGBgIC0tPgoKIyMjVG9wIDIwIEJyZWVkIENvbWJvcwoKYGBge3J9CgpzZWF0dGxlX3BldF9saWNlbnNlcyRwcmltYXJ5X2JyZWVkIDwtIHN1YigiKFxcdyspLFxccyhcXHcrKSIsIlxcMiBcXDEiLCBzZWF0dGxlX3BldF9saWNlbnNlcyRwcmltYXJ5X2JyZWVkKQoKc2VhdHRsZV9wZXRfbGljZW5zZXMkc2Vjb25kYXJ5X2JyZWVkIDwtIHN1YigiKFxcdyspLFxccyhcXHcrKSIsIlxcMiBcXDEiLCBzZWF0dGxlX3BldF9saWNlbnNlcyRzZWNvbmRhcnlfYnJlZWQpCgpwYWwgPC0gcmV2KGJleW9uY2VfcGFsZXR0ZSgyMiwgMTAwLCB0eXBlID0gImNvbnRpbnVvdXMiKSkKCnNlYXR0bGVfcGV0X2xpY2Vuc2VzICU+JSAKICBmaWx0ZXIoc3BlY2llcyA9PSAiRG9nIikgJT4lIAogIHNlbGVjdChwcmltYXJ5X2JyZWVkLCBzZWNvbmRhcnlfYnJlZWQpICU+JSAKICB1bmlxdWUoKSAlPiUgCiAgbXV0YXRlKGlkID0gcm93X251bWJlcigpKSAlPiUgCiAgbGVmdF9qb2luKGRvZ19icmVlZF9jaGFyYWN0ZXJpc3RpY3MsIGJ5ID0gYygicHJpbWFyeV9icmVlZCIgPSAiQnJlZWROYW1lIikpICU+JSAKICBzZWxlY3QocHJpbWFyeV9icmVlZCwgc2Vjb25kYXJ5X2JyZWVkLCBpZCwgVGVtcGVybWVudCkgJT4lIAogIHJlbmFtZSgicHJpbWFyeV90ZW1wIiA9IFRlbXBlcm1lbnQpICU+JSAKICBsZWZ0X2pvaW4oZG9nX2JyZWVkX2NoYXJhY3RlcmlzdGljcywgYnkgPSBjKCJzZWNvbmRhcnlfYnJlZWQiID0gIkJyZWVkTmFtZSIpKSAlPiUgCiAgc2VsZWN0KHByaW1hcnlfYnJlZWQsIHNlY29uZGFyeV9icmVlZCwgaWQsIHByaW1hcnlfdGVtcCwgVGVtcGVybWVudCkgJT4lIAogIHJlbmFtZSgic2Vjb25kYXJ5X3RlbXAiID0gVGVtcGVybWVudCkgJT4lIAogIG11dGF0ZSh0ZW1wX2NvbWJvID0gcGFzdGUocHJpbWFyeV90ZW1wLCBzZWNvbmRhcnlfdGVtcCwgc2VwID0gIiwgIikpICU+JSAKICBzZWxlY3QoLXByaW1hcnlfdGVtcCwgLXNlY29uZGFyeV90ZW1wKSAlPiUgCnVubmVzdF90b2tlbnMod29yZCwgdGVtcF9jb21ibykgJT4lIAogIG11dGF0ZShicmVlZF9jb21ibyA9IHBhc3RlKHByaW1hcnlfYnJlZWQsIHNlY29uZGFyeV9icmVlZCwgc2VwID0gIiArICIpKSAlPiUgCiAgZHJvcF9uYSgpICU+JSAKICBzZWxlY3QoLXByaW1hcnlfYnJlZWQsIC1zZWNvbmRhcnlfYnJlZWQpICU+JSAKICBpbm5lcl9qb2luKEFGSU5OKSAlPiUgCiAgZ3JvdXBfYnkoaWQsIGJyZWVkX2NvbWJvKSAlPiUgCiAgc3VtbWFyaXNlKGF2Z19zY29yZSA9IG1lYW4oc2NvcmUpLAogICAgICAgICAgICBzY29yZSA9IHN1bShzY29yZSkpICU+JSAKICBhcnJhbmdlKGRlc2MoYXZnX3Njb3JlKSkgJT4lIAogIHVuZ3JvdXAoKSAlPiUgCiAgbXV0YXRlKHJhbmsgPSBkZW5zZV9yYW5rKC1hdmdfc2NvcmUpKSAlPiUgCiAgaGVhZCgyMCkgJT4lIAogIGdncGxvdChhZXMoeCA9IHJlb3JkZXIoYnJlZWRfY29tYm8sIGF2Z19zY29yZSksIHkgPSBhdmdfc2NvcmUsIGZpbGwgPSBzY29yZSkpICsKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikgKyAKICBnZW9tX3RleHQoc3RhdCA9ICJpZGVudGl0eSIsIHNpemUgPSAzLCBoanVzdCA9IC0xLCBhZXMobGFiZWwgPSByYW5rKSkgKyAKICBzY2FsZV9maWxsX2dyYWRpZW50bihjb2xvdXJzID0gcGFsLCBuYW1lID0gIlNjb3JlIikgKwogIGNvb3JkX2ZsaXAoKSArIAogIHhsYWIoIkJyZWVkIikgKyAKICB5bGFiKCJTY29yZSIpCiAgCgpgYGAKCiMjI0J5IEdyb3VwIAoKTGV0J3MgaGVhciBpdCBmb3IgdGhlIHRoZSBIb3VuZHMhIAoKYGBge3J9Cgpncm91cF9zY29yZXMgPSBkb2dfd29yZHMgJT4lIAogIGZpbHRlcighR3JvdXAxID09ICJTb3V0aGVybiIpICU+JSAKICBzZWxlY3QoR3JvdXAxLCB3b3JkKSAlPiUgCiAgdW5pcXVlKCkgJT4lIAogIGlubmVyX2pvaW4oQUZJTk4pICU+JSAKICBncm91cF9ieShHcm91cDEpICU+JSAKICBzdW1tYXJpc2UoYXZnX3Njb3JlID0gbWVhbihzY29yZSksCiAgICAgICAgICAgIHNjb3JlID0gc3VtKHNjb3JlKSkgJT4lIAogIGRyb3BfbmEoKQoKZ3JvdXBfc2NvcmVzICU+JSAKICBhcnJhbmdlKGRlc2MoYXZnX3Njb3JlKSkgJT4lIAogICNoZWFkKDIwKSAlPiUgCiAgZ2dwbG90KGFlcyh4ID0gcmVvcmRlcihHcm91cDEsIGF2Z19zY29yZSksIHkgPSBhdmdfc2NvcmUsIGZpbGwgPSBzY29yZSkpICsKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikgKyAKICBzY2FsZV9maWxsX2dyYWRpZW50bigKICAgIGNvbG91cnMgPSBwYWwsIG5hbWUgPSAiVG90YWwgU2NvcmUiKSArCiAgY29vcmRfZmxpcCgpICsgCiAgeGxhYigiQnJlZWQgR3JvdXAiKSArIAogIHlsYWIoIk1lYW4gU2NvcmUiKQoKYGBgCgojIyNCcmVlZCBHcm91cCAxICsgR3JvdXAgMgoKYGBge3J9Cgpncm91cF9ib3RoID0gZG9nX3dvcmRzICU+JSAKICBmaWx0ZXIoIUdyb3VwMSA9PSAiU291dGhlcm4iKSAlPiUgCiAgaW5uZXJfam9pbihBRklOTikgJT4lIAogIG11dGF0ZShncm91cF9jb21ibyA9IHBhc3RlKEdyb3VwMSwgR3JvdXAyLCBzZXAgPSAiICsgIikpICU+JSAKICBncm91cF9ieShncm91cF9jb21ibykgJT4lIAogIHN1bW1hcmlzZSgKICAgIGF2Z19zY29yZSA9IG1lYW4oc2NvcmUpLCAKICAgIHNjb3JlID0gc3VtKHNjb3JlKQogICAgKSAlPiUgCiAgI3N1bW1hcmlzZShjb3VudCA9IG5fZGlzdGluY3Qod29yZCkpICU+JSAKICBkcm9wX25hKCkKCmdyb3VwX2JvdGggJT4lIAogIGFycmFuZ2UoZGVzYyhhdmdfc2NvcmUpKSAlPiUgCiAgI2hlYWQoMjApICU+JSAKICBnZ3Bsb3QoYWVzKHggPSByZW9yZGVyKGdyb3VwX2NvbWJvLCBhdmdfc2NvcmUpLCB5ID0gYXZnX3Njb3JlLCBmaWxsID0gc2NvcmUpKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpICsgCiAgc2NhbGVfZmlsbF9ncmFkaWVudG4oCiAgICBjb2xvdXJzID0gcGFsLCBuYW1lID0gIlRvdGFsIFNjb3JlIikgKwogIGNvb3JkX2ZsaXAoKSArIAogIHhsYWIoIkJyZWVkIEdyb3VwIDEgKyBCcmVlZCBHcm91cCAyIikgKyAKICB5bGFiKCJNZWFuIFNjb3JlIikKCmBgYAoKIyMjSW50ZWxsaWdlbmNlIFJhdGluZ3MKRG9lcyBpbnRlbGxpZ2VuY2UgY29ycmVzcG9uZCB3aXRoIHByaWNlPyBOby4gCgpgYGB7cn0KCmRvZ19icmVlZF9jaGFyYWN0ZXJpc3RpY3MgJT4lIAogIHNlbGVjdChCcmVlZE5hbWUsIEludGVsbGlnZW5jZSwgQXZnUHVwUHJpY2UpICU+JSAKICBhcnJhbmdlKGRlc2MoSW50ZWxsaWdlbmNlKSkgJT4lIAogIGhlYWQoMzApICU+JSAKICBtdXRhdGUoQnJlZWROYW1lID0gcmVvcmRlcihCcmVlZE5hbWUsIC1JbnRlbGxpZ2VuY2UpKSAlPiUgCiAgZHJvcF9uYSgpICU+JSAKICBwbG90X2x5KAogICAgeCA9IH5CcmVlZE5hbWUKICAgICwgeSA9IH5JbnRlbGxpZ2VuY2UKICAgICwgdHlwZSA9ICJiYXIiCiAgICAsIG5hbWUgPSB+cGFzdGUoIkludGVsbGlnZW5jZSIpCiAgICAsIGhlaWdodCA9IDUwMAogICAgLCB3aWR0aCA9IDEwMDAKICAgICMsIGxlZ2VuZGdyb3VwID0gfkludGVsbGlnZW5jZQogICAgLCB0ZXh0ID0gfnBhc3RlKAogICAgICAgICJCcmVlZDoiLCBCcmVlZE5hbWUKICAgICAgICAsICI8YnI+SW50ZWxsaWdlbmNlIFJhdGluZzoiLCBJbnRlbGxpZ2VuY2UKICAgICAgICAsICI8YnI+QXZlcmFnZSBQdXBweSBQcmljZToiLCBkb2xsYXIoQXZnUHVwUHJpY2UpCiAgICAgICAgKQogICAgLCBob3ZlcmluZm8gPSAndGV4dCcKICApICU+JSAKICBhZGRfdHJhY2UoCiAgICB5ID0gfkF2Z1B1cFByaWNlCiAgICAsIHR5cGUgPSAic2NhdHRlciIKICAgICwgbW9kZSA9ICJsaW5lcyttYXJrZXJzIgogICAgLCB5YXhpcyA9ICJ5MiIKICAgICwgbmFtZSA9IH5wYXN0ZSgiQXZnIFByaWNlIikKICApICU+JSAKICBsYXlvdXQoCiAgICB5YXhpczIgPSBsaXN0KAogICAgICBzaWRlID0gInJpZ2h0IgogICAgICAsIG92ZXJsYXlpbmcgPSAieSIKICAgICAgLCB0aXRsZSA9ICJBdmcgRG9nIFByaWNlICQiCiAgICApCiAgICAsIHhheGlzID0gbGlzdCgKICAgICAgdGlja2FuZ2xlID0gMzAKICAgICAgLCB0aXRsZSA9ICJCcmVlZCIKICAgICkKICApCiAgCgpgYGAKCgojI1BldHMgYnkgWmlwIFlPWQpgYGB7cn0KCnNlYXR0bGVfcGV0X2xpY2Vuc2VzICU+JSAKICAgIGRyb3BfbmEoKSAlPiUgCiAgc2VsZWN0KGxpY2Vuc2VfaXNzdWVfZGF0ZQogICAgICAgICAsIHNwZWNpZXMKICAgICAgICAgLCB6aXBfY29kZQogICAgICAgICAsIGxpY2Vuc2VfbnVtYmVyKSAlPiUgCiAgbXV0YXRlKGxpY2Vuc2VfaXNzdWVfZGF0ZSA9IGFzLkRhdGUobGljZW5zZV9pc3N1ZV9kYXRlKSwKICAgICAgICAgeWVhciA9IGx1YnJpZGF0ZTo6eWVhcihsaWNlbnNlX2lzc3VlX2RhdGUpLAogICAgICAgICB6aXBfY29kZSA9IChhcy5mYWN0b3IoemlwX2NvZGUpKQogICAgICAgICApICU+JQogIGdyb3VwX2J5KHllYXIsIHppcF9jb2RlLCBzcGVjaWVzKSAlPiUgCiAgc3VtbWFyaXNlKG5ld19wZXRzID0gbl9kaXN0aW5jdChsaWNlbnNlX251bWJlcikpICU+JSAKICB1bmdyb3VwKCkgJT4lIAogIHBsb3RfbHkoCiAgeCA9IH56aXBfY29kZQogICwgeSA9IH5uZXdfcGV0cwogICwgY29sb3IgPSB+c3BlY2llcwogICwgdHlwZSA9ICJiYXIiCiAgLCBsZWdlbmRfZ3JvdXAgPSB+c3BlY2llcwogICwgZnJhbWUgPSB+eWVhcgogICkgJT4lIAogIGxheW91dCgKICAgIGJhcm1vZGUgPSAic3RhY2siCiAgICAsIHhheGlzID0gbGlzdCh0aXRsZSA9ICJaaXAgQ29kZSIpCiAgICAsIHlheGlzID0gbGlzdCh0aXRsZSA9ICJOZXcgUGV0cyIpCiAgKQoKYGBgCgoK